﻿using System;
using System.Runtime.InteropServices;

namespace Eflong.Structure
{
    public interface IStruct<T>
    {
        int Count( );

        int Size( );

        //public static T PtrToStruct<T>(this T t, byte[] ba, ref int pos);
    }

    public static class StructConvert
    {
        public static int Size<T , TItem>( this IStruct<T> t , TItem[ ] items )
        {
            int size = t.Size( ) + ( t.Count( ) - 1 ) * Marshal.SizeOf( items[ 0 ] );

            return size;
        }

        public static byte[ ] GetBytes<T , TBase , TItem>( this IStruct<T> t , TBase tB , TItem[ ] items )
        {
            int size = t.Size<T , TItem>( items );
            byte[ ] ba = new byte[ size ];
            Array.Copy( StructConvert.StructToByteArrar<TBase>( tB ) , ba , Marshal.SizeOf( tB ) );

            int pos = Marshal.SizeOf( tB );
            int sizen = Marshal.SizeOf( items[ 0 ] );
            for ( int n = 0; n < t.Count( ); n++ )
            {
                Array.Copy( StructConvert.StructToByteArrar<TItem>( items[ n ] ) , 0 , ba , pos , sizen );
                pos += sizen;
            }
            return ba;
        }

        public static int Size<T>( this T t )
        {
            return Marshal.SizeOf( t );
        }

        public static byte[ ] GetBytes<T>( this T t )
        {
            return GetBytes<T>( t , false );
        }

        /// <summary>
        /// 转换为字节数组
        /// </summary>
        /// <typeparam name="T">结构类型</typeparam>
        /// <param name="t">结构实例</param>
        /// <param name="fDeleteOld">是否删除旧结构</param>
        /// <returns></returns>
        public static byte[ ] GetBytes<T>( this T t , bool fDeleteOld )
        {
            byte[ ] ba = new byte[ t.Size<T>( ) ];
            IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement( ba , 0 );
            Marshal.StructureToPtr( t , ptr , fDeleteOld );
            return ba;
        }

        /// <summary>
        /// 把字节数组转换为结构
        /// </summary>
        /// <typeparam name="T">结构类型</typeparam>
        /// <param name="t">结构实例</param>
        /// <param name="ba">字节数组</param>
        /// <param name="pos">起始位置</param>
        /// <returns>返回转换后的结构实例</returns>
        public static T PtrToStruct<T>( this T t , byte[ ] ba , ref int pos )
        {
            IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement( ba , pos );
            t = ( T )Marshal.PtrToStructure( ptr , typeof( T ) );
            pos += Marshal.SizeOf( t );
            return t;
        }

        /// <summary>
        /// 获取结构的大小
        /// </summary>
        /// <param name="st"></param>
        /// <returns></returns>
        public static int SizeOf<T>( T st )
        {
            return Marshal.SizeOf( st );
        }

        /// <summary>
        /// 从数组转换成结构
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="ba"></param>
        /// <returns></returns>
        public static T PtrToStruct<T>( byte[ ] ba )
        {
            int pos = 0;
            return PtrToStruct<T>( ba , ref pos );
        }

        public static T PtrToStruct<T>( byte[ ] ba , ref int pos )
        {
            IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement( ba , pos );
            T t = ( T )Marshal.PtrToStructure( ptr , typeof( T ) );
            pos += Marshal.SizeOf( t );
            return t;
        }

        public static T PtrToStruct<T>( IntPtr ptr , ref int pos )
        {
            //IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement(ba, pos);
            T t = ( T )Marshal.PtrToStructure( ptr , typeof( T ) );
            pos += Marshal.SizeOf( t );
            return t;
        }

        public static T PtrToStruct<T>( IntPtr ptr )
        {
            //IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement(ba, pos);
            T t = ( T )Marshal.PtrToStructure( ptr , typeof( T ) );
            return t;
        }

        /// <summary>
        /// 从数组转换成结构
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="ptr">字节数组</param>
        /// <param name="pos">起始位置</param>
        /// <returns></returns>
        public static T PtrToStruct<T>( IntPtr ptr , ref int pos , int size )
        {
            T t = ( T )Marshal.PtrToStructure( ptr , typeof( T ) );
            pos += size;
            return t;
        }

        /// <summary>
        /// 从数组转换成结构
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="ba">字节数组</param>
        /// <param name="pos">起始位置</param>
        /// <returns></returns>
        public static T PtrToStruct<T>( byte[ ] ba , ref int pos , int size )
        {
            IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement( ba , pos );
            T t = ( T )Marshal.PtrToStructure( ptr , typeof( T ) );
            pos += size;
            return t;
        }

        /// <summary>
        /// 从结构转换到数组
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <returns></returns>
        public static byte[ ] StructToByteArrar<T>( T t )
        {
            return StructToByteArrar( t , true );
        }

        public static byte[ ] StructToByteArrar<T>( T t , bool fDeleteOld )
        {
            int size = Marshal.SizeOf( t );
            byte[ ] ba = new byte[ size ];
            GCHandle handle = GCHandle.Alloc( ba , GCHandleType.Pinned );
            IntPtr ptr = handle.AddrOfPinnedObject( );
            Marshal.StructureToPtr( t , ptr , fDeleteOld );
            handle.Free( );
            return ba;
        }
    }
}